package com.gton.lpl.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gton.lpl.common.annotation.NeedTokenByJWT;
import com.gton.lpl.common.annotation.SkipTokenByJWT;
import com.gton.lpl.common.pojo.RedisKeys;
import com.gton.lpl.common.pojo.Resp;
import com.gton.lpl.common.pojo.ThisSystemSign;
import com.gton.lpl.common.tools.IPAndMacTools;
import com.gton.lpl.common.tools.RSAUtil;
import com.gton.lpl.entity.GpRule;
import com.gton.lpl.entity.Gplogin;
import com.gton.lpl.service.GpRuleService;
import com.gton.lpl.service.GploginService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description: 登录控制器
 * <p>关于AfterScript更详细的使用方法及介绍
 * //ke = knife4jenvironment
 * var code=ke.response.data.code;
 * console.log(code)
 * if(code=='200'){
 * //获取token
 * var token=ke.response.data.data;
 * //设置token到请求头
 * ke.global.setHeader("token",token);
 * }
 * <p>
 * 前端参数用户密码加密后传入后端，后端解密比较是否满足验签，解密还原原始参数
 * 原始参数进入service继续使用后端自定义的AES不可逆加密，加密后进入Mapper正式比对数据库数据
 * 实际数据库存入的密码值不是前端的实际密码值，也不是前端实际密码值得前端加密密码值。
 * @author: GuoTong
 * @createTime: 2021-10-05 16:06
 * @since JDK 1.8 OR 11
 **/
@RestController
@Slf4j
@Api(tags = {"登录控制器"})
public class LoginController {

    @Autowired
    private GploginService gploginService;

    @Autowired
    private ThisSystemSign thisSystemSign;

    @Autowired
    private GpRuleService gpRuleService;


    @Autowired
    @Qualifier("redisTemplateByMySelf")
    private RedisTemplate redisTemplate;

    @GetMapping(value = {"/login", "/login.html", "/login.jsp", "/login.ftl", "/", ""}, name = "登录页面访问")
    @ApiOperation(value = "登录页面访问", notes = "页面地址跳转", hidden = true)
    @SkipTokenByJWT
    public ModelAndView loginGotoPage(HttpServletRequest request) {
        //传入公钥提供给前端，允许前端参数通过公钥加密后传入后端
        HttpSession session = request.getSession();
        if (session != null) {
            session.setAttribute("sessionKeys", RedisKeys.pubKey);
        }
        return new ModelAndView("login");
    }


    /**
     * Description:  原始数据-数据库层次加密：目的比对数据库的值
     *
     * @author: GuoTong
     * @date: 2022-02-12 19:57:33
     * @return:
     */
    @SkipTokenByJWT
    @ApiOperation(value = "实际密码数据层层加密后", hidden = true)
    @GetMapping(value = "/getDBPwD", name = "实际密码数据层层加密后")
    public Resp<Object> passwordCountersignByDB(@RequestParam String keySign) {
        String signThisUserKey = thisSystemSign.getDataSignByDb(keySign);
        return Resp.Ok(signThisUserKey);
    }

    /**
     * Description:  原始数据库层次加密的数据的值解密：目的忘记密码反解密查看说的数据库真实数据原始值
     *
     * @author: GuoTong
     * @date: 2022-02-12 19:57:33
     */
    @SkipTokenByJWT
    @ApiOperation(value = "实际密码数据层层加密后数据反解密", hidden = true)
    @GetMapping(value = "/getDBPwDCallBack", name = "实际密码数据层层加密后反解密")
    public Resp<Object> getDBPwDCallBack(@RequestParam String keySign) {
        String signThisUserKey = thisSystemSign.getDataSignByDbCallBack(keySign);
        return Resp.Ok(signThisUserKey);
    }


    /**
     * Description: 前端层次传入的密码是加密后的数据，用做后端验签名
     *
     * @author: GuoTong
     * @date: 2022-02-12 19:59:18
     * @return:
     */
    @SkipTokenByJWT
    @ApiOperation(value = "01实际前端层密码加签校验", tags = "01")
    @GetMapping(value = "/pwdGetSin", name = "实际前端层密码加签校验")
    @ApiResponse(code = 200, message = "返回加签后的:password:数据", response = Resp.class)
    public Resp<String> passwordCountersign(@RequestParam String keySign) {
        String signThisUserKey = thisSystemSign.getThisUserKey(keySign);
        return Resp.Ok(signThisUserKey);
    }


    /**
     * Description: 前端层次传入的密码是加密后的数据用作解密，查看具体传入的参数是什么
     *
     * @author: GuoTong
     * @date: 2022-02-12 20:00:18
     * @return:
     */
    @SkipTokenByJWT
    @PostMapping(value = "/paramSign", name = "请求参数加密解密Test")
    @ApiOperation(value = "请求参数加密数据·get解密后数据", notes = "insomnia测试", hidden = true)
    public Resp<Object> paramSign(@RequestBody Map<String, Object> map) {
        Object getsign = map.get("getsign");
        String withPrivate = "";
        //私钥解密
        try {
            withPrivate = RSAUtil.decryptByPrivateKey(RSAUtil.priKey, getsign.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Resp.Ok(withPrivate);
    }

    /**
     * Description: 根据用户名获取数据库密码，数据库密码并解析为原始数据
     *
     * @author: GuoTong
     * @date: 2022-02-12 20:00:18
     * @return:
     */
    @NeedTokenByJWT
    @PostMapping(value = "/getUserPassWordByName", name = "根据用户名获取数据库密码")
    @ApiOperation(value = "根据用户名获取数据库密码", notes = "insomnia测试", hidden = true)
    @ApiResponse(code = 200, message = "不对外开放", response = Resp.class)
    public Resp<String> getNotSignPwdByDBInName(@RequestBody Map<String, Object> map) {
        Object getsign = map.get("getsign");
        String password = "";
        try {
            //查询数据库密码
            QueryWrapper<Gplogin> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("username", getsign);
            Gplogin gplogin = gploginService.getOne(queryWrapper);
            String passwordSign = gplogin.getPassword();
            password = thisSystemSign.getDataSignByDbCallBack(passwordSign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Resp.Ok(password);
    }

    /**
     * Description:  真实用户登录接口
     *
     * @author: GuoTong
     * @date: 2022-02-12 19:59:52
     * @return:
     */
    @SkipTokenByJWT
    @ApiOperation(value = "02登录请求", notes = "登录json格式请求")
    @PostMapping(value = {"/logging"})
    @ApiResponse(code = 200, message = "返回的data是token", response = Resp.class)
    public Resp<String> loginRequest(@RequestBody Gplogin gplogin, HttpServletRequest request, HttpServletResponse response) throws SocketException, UnknownHostException {
        //AES解密密码
        String password = gplogin.getPassword();
        try {
            //私钥解密
            String aesBackPwd = RSAUtil.decryptByPrivateKey(RSAUtil.priKey, password);
            //还原传入的实际参数值
            gplogin.setPassword(aesBackPwd);
        } catch (Exception e) {
            log.error("密码解密失败错误，验证签名加密是否合法");
            return Resp.error("密码解密失败错误，验证签名加密是否合法");
        }
        //判断是否输入正确
        Gplogin userTable = gploginService.queryForEntity(gplogin);
        if (userTable == null) {
            return Resp.error("用户名或者密码错误");
        }
        //用户名用于回显到页面
        HttpSession session = request.getSession();
        String id = session.getId();
        log.info("SessionId->" + id);
        session.setAttribute("userinfo", userTable);
        String token = userTable.getToken(userTable);
        //缓存登录状态
        String loginName = userTable.getUsername();
        //获取请求的IP地址
        String ipAddr = IPAndMacTools.getIpAddr(request);
        //获取请求的Mac地址
        String localMac = IPAndMacTools.getLocalMac(ipAddr);
        //存入mac+name,mac去除：
        String IsRedisKeyByLifeName = localMac.replace(":", "").concat("#" + loginName);
        Map dataRedis = redisTemplate.opsForHash().entries(IsRedisKeyByLifeName);
        if (dataRedis == null || dataRedis.size() == 0) {
            //Redis存两样东西，一个是用户权限，一个是用户token
            QueryWrapper<GpRule> wrapper = new QueryWrapper<>();
            wrapper.eq("login_id", userTable.getId());
            GpRule gpRule = gpRuleService.getOne(wrapper);
            Map<String, Object> map = new HashMap<>();
            map.put("rule", gpRule.getRoleGrade());
            map.put("token", token);
            redisTemplate.opsForHash().putAll(IsRedisKeyByLifeName, map);
            //设置过期时间； TimeUnit.MILLISECONDS  毫秒:设置默认时间是SECONDS秒：60秒
            redisTemplate.expire(IsRedisKeyByLifeName, 60, TimeUnit.MINUTES);

            //客户端存放cookie
            Cookie cookie = new Cookie("token", token);
            cookie.setMaxAge(60 * 60);
            response.addCookie(cookie);
        }
        return Resp.Ok(token);
    }


    @NeedTokenByJWT(rule = 3000)
    @GetMapping(value = "/getALl", name = "需要登录权限访问所有用户")
    @ApiOperation(value = "获取所有用户信息", tags = "需要token")
    @ApiResponses(@ApiResponse(code = 200, message = "查询登录用户信息ALL", response = Resp.class))
    public Resp<List<Gplogin>> getAll() {
        List<Gplogin> list = gploginService.list();
        return Resp.Ok(list);
    }


    @GetMapping("/getCaprcha")
    @ApiOperation(value = "滑动验证码", hidden = true)
    public ModelAndView getCaprcha() {
        return new ModelAndView("/common/Caprcha");
    }

}
